#ifndef _SQTABLE_H_
#define _SQTABLE_H_

#include "sqstring.h"

#define hashptr(p)  ((SQHash)(((SQInteger)p) >> 3))

inline SQHash HashObj( const SQObjectPtr &key ) {
  switch( type( key ) ) {
    case OT_STRING:
      return _string( key )->_hash;
    case OT_FLOAT:
      return ( SQHash )( ( SQInteger )_float( key ) );
    case OT_BOOL:
    case OT_INTEGER:
      return ( SQHash )( ( SQInteger )_integer( key ) );
    default:
      return hashptr( key._unVal.pRefCounted );
  }
}

struct SQTable : public SQDelegable {
  private:
    struct _HashNode {
      _HashNode() { next = NULL; }
      SQObjectPtr val;
      SQObjectPtr key;
      _HashNode *next;
    };
    _HashNode *_firstfree;
    _HashNode *_nodes;
    SQInteger _numofnodes;
    SQInteger _usednodes;

    void AllocNodes( SQInteger nSize );
    void Rehash( bool force );
    SQTable( SQSharedState *ss, SQInteger nInitialSize );
    void _ClearNodes();
  public:
    static SQTable* Create( SQSharedState *ss, SQInteger nInitialSize ) {
      SQTable *newtable = ( SQTable* )SQ_MALLOC( sizeof( SQTable ) );
      new( newtable ) SQTable( ss, nInitialSize );
      newtable->_delegate = NULL;
      return newtable;
    }
    void Finalize();
    SQTable *Clone();
    ~SQTable() {
      SetDelegate( NULL );
      REMOVE_FROM_CHAIN( &_sharedstate->_gc_chain, this );
      for( SQInteger i = 0; i < _numofnodes; i++ ) {
        _nodes[i].~_HashNode();
      }
      SQ_FREE( _nodes, _numofnodes * sizeof( _HashNode ) );
    }
    #ifndef NO_GARBAGE_COLLECTOR
    void Mark( SQCollectable **chain );
    #endif
    inline _HashNode *_Get( const SQObjectPtr &key, SQHash hash ) {
      _HashNode *n = &_nodes[hash];
      do {
        if( _rawval( n->key ) == _rawval( key ) && type( n->key ) == type( key ) ) {
          return n;
        }
      } while( ( n = n->next ) );
      return NULL;
    }
    bool Get( const SQObjectPtr &key, SQObjectPtr &val );
    void Remove( const SQObjectPtr &key );
    bool Set( const SQObjectPtr &key, const SQObjectPtr &val );

    bool NewSlot( const SQObjectPtr &key, const SQObjectPtr &val );
    SQInteger Next( bool getweakrefs, const SQObjectPtr &refpos, SQObjectPtr &outkey, SQObjectPtr &outval );

    SQInteger CountUsed() { return _usednodes;}
    void Clear();
    void Release() {
      sq_delete( this, SQTable );
    }

};

#endif
